<?php

declare(strict_types=1);

namespace MultiWorker;

use RuntimeException;

class Signal
{
    /**
     * 重启
     * 
     * @var integer
     */
    const STATUS_RELOAD = 1;

    /**
     * 停止
     * 
     * @var integer
     */
    const STATUS_STOP = 2;

   /**
     * linux signal list. 
     *
     * @var array
     */
    protected $signalSupport = [];

    /**
     * on event list.
     *
     * @var array
     */
    protected $events;

    /**
     * 是否打开异步信号
     * 
     * @var boolean
     */
    protected $openAsyncSignal = false;

    /**
     * Signal Constructor,
     * 
     * @return void
     */
    public function __construct()
    {
        $this->signalSupport = [
            'reload'    => SIGUSR1,
			'stop'	    => SIGUSR2,
			'terminate' => SIGTERM,
			'int'		=> SIGINT
        ];
    }

    /**
     * 开启异步信号
     * 
     * @return self
     */
    public function openAsyncSignal ($async = false): void
    {
        if (!$this->isSupportAsyncSignal()) {
            $async = false;
        }
        $this->openAsyncSignal = $async;
        $this->openAsyncSignal && pcntl_async_signals($async);
    }

    /**
     * 获取是否支持异步信号
     * 
     * @return boolean
     */
    public function isSupportAsyncSignal (): bool
    {
        return function_exists('pcntl_async_signals');
    }

    /**
     * dispatch signal for the handlers
     *
     * @return void
     */
    public function dispatch (): void
    {
        !$this->openAsyncSignal && pcntl_signal_dispatch();
    }

    /**
     * 注册信号监听
     *
     * @return void
     */
    public function register ()
    {
        foreach ($this->signalSupport as $signal) {
            pcntl_signal($signal,function () use ($signal) {
                $this->triggerSignal($signal);
            });
        }
    }

    /**
     * Signal __get.
     *
     * @param string $name
     * @return mixed
     */
    public function __get ($name)
    {
        return $this->signalSupport[$name] ?? null;
    }

    /**
     * 触发信号监听
     *
     * @param integer $signal
     * @return void
     */
    public function triggerSignal ($signal = 0)
    {
        switch ($signal) {
            case $this->signalSupport['reload']:
                $this->triggerEvent('reload',$signal);
                break;
            case $this->signalSupport['stop']:
                $this->triggerEvent('stop',$signal);
                break;
            case $this->signalSupport['terminate']:
                $this->triggerEvent('terminate',$signal);
                break;
            case $this->signalSupport['int']:
                $this->triggerEvent('int',$signal);
                break;
            default:
                $this->triggerEvent('miss',$signal);
                break;
        }
    }

    /**
     * 注册监听事件
     *
     * @param mixed $name
     * @param mixed $handler
     * @return self
     */
    public function on ($name,$handler = null): self
    {
        if (is_array($name)) {
            foreach ($name as $event => $callback) {
                $this->on($event,$callback);
            }
        } else {
            if (!is_callable($handler)) {
                throw new RuntimeException("signal on event handler is not an executable object.");
            }
            $this->events[$name] = $handler;
        }
        return $this;
    }

    /**
     * 触发事件
     *
     * @param string $name
     * @param array $arguments
     * @return mixed
     */
    public function triggerEvent (string $name,...$arguments)
    {
        $event = $this->events[$name] ?? function () {};
        call_user_func($event,...$arguments);
    }
}